#pragma once
#include <iostream>
#include <pthread.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include "Log.hpp"

int back_log = 5;

class TcpServer{
private:
    int _listen_sock;
    int _port;
    static TcpServer* _instance;
private:
    TcpServer(int port):_port(port), _listen_sock(-1){}
    TcpServer(const TcpServer&) = delete;
    TcpServer& operator=(const TcpServer&) = delete;
    bool Sock(){
        _listen_sock = socket(AF_INET, SOCK_STREAM, 0);
        if(-1 == _listen_sock){
            LOG(FATAL, "Sock error!");
            return false;
        }
        int opt = 1;
        setsockopt(_listen_sock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));
        return true;
    }

    bool Bind(){
        sockaddr_in server;
        server.sin_family = AF_INET;
        server.sin_port = htons(_port);
        server.sin_addr.s_addr = INADDR_ANY;
        if(-1 == bind(_listen_sock, (sockaddr*)&server, sizeof(server))){
            LOG(FATAL, "Bind error!");
            return false;
        }
        return true;
    }

    bool Listen(){
        if(-1 == listen(_listen_sock, back_log)){
            LOG(FATAL, "Bind error!");
            return false;
        }
        return true;
    }

public:
    static TcpServer* GetInstance(int port){
        if(nullptr == _instance){
            pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
            pthread_mutex_lock(&lock);
            if(nullptr == _instance){
                _instance = new TcpServer(port);
                _instance->InitServer();
            }
            pthread_mutex_unlock(&lock);
        }
        return _instance;
    }

    bool InitServer(){
        if(Sock() && Bind() && Listen()){
            return true;
        }
        exit(1);
    }

    bool Accept(int* sock){
        sockaddr_in client;
        socklen_t len;
        if(-1 == (*sock = accept(_listen_sock, (sockaddr*)&client, &len))){
            LOG(FATAL, "Accept error!");
            return false;
        }
        return true;
    }

    ~TcpServer(){
        if(-1 != _listen_sock){
            close(_listen_sock);
        }
    }
};

TcpServer* TcpServer::_instance = nullptr;